#include "ParamsLoader.h"
#include <fstream>
#include "Structure.h"
#include <iostream>

using namespace std;
using namespace DL;
float GetAccurateValue(Json::Value value);

void ParamsLoader::LoadFromFile(string path)
{
	ifstream ifile(path);
	if (!ifile.is_open()) {
		throw "open file failed";
	}
	char ch;
	ostringstream buf;
	while (buf && ifile.get(ch)) {
		buf.put(ch);
	}
	LoadFromString(buf.str());
}

void ParamsLoader::LoadFromString(string file)
{
	Json::CharReaderBuilder ReaderBuilder;
	ReaderBuilder["emitUTF8"] = true;
	unique_ptr<Json::CharReader> charread(ReaderBuilder.newCharReader());
	string strerr;
	bool isok = charread->parse(file.c_str(), file.c_str() + file.size(), &value, &strerr);
	if (!isok || strerr.size() != 0) {
		throw "json decode failed";
	}
}

Json::Value ParamsLoader::GetLayerValueByName(string name) {
	int size = value.size();
	for (int i = 0; i < size; i++)
	{
		if (value[i]["layer"] == name) {
			return value[i]["value"];
		}
	}
	throw "Error layer name";
}

const Tensor1D<float>& ParamsLoader::LoadTensor1DFloat(string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int width = weight.size();
	DL::Tensor1D<float>* params = new Tensor1D<float>(width);

	for (int x = 0; x < width; x++)
	{
		float value = GetAccurateValue(weight[x]);
		params->Set(x, value);
	}
	return *params;
}

const DL::Tensor2D<float>& ParamsLoader::LoadTensor2DFloat(string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int height = weight.size();
	int width = weight[0].size();
	DL::Tensor2D<float>* params = new Tensor2D<float>(width, height);

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			float value = GetAccurateValue(weight[y][x]);
			params->Set(x, y, value);
		}
	}
	return *params;
}

const DL::Tensor3D<float>& ParamsLoader::LoadTensor3DFloat(string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int channel = weight.size();
	int height = weight[0].size();
	int width = weight[0][0].size();
	DL::Tensor3D<float>* params = new Tensor3D<float>(width, height, channel);
	for (int c = 0; c < channel; c++)
	{
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				float value = GetAccurateValue(weight[c][y][x]);
				params->Set(x, y, c, value);
			}
		}
	}
	return *params;
}

const DL::Tensor4D<float>& ParamsLoader::LoadTensor4DFloat(string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int deepth = weight.size();
	int channel = weight[0].size();
	int height = weight[0][0].size();
	int width = weight[0][0][0].size();
	DL::Tensor4D<float>* params = new DL::Tensor4D<float>(width, height, channel, deepth);

	for (int d = 0; d < deepth; d++)
	{
		for (int c = 0; c < channel; c++)
		{
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					float value = GetAccurateValue(weight[d][c][y][x]);
					params->Set(x, y, c, d, value);
				}
			}
		}
	}
	return *params;
}

const DL::Tensor1D<int8>& ParamsLoader::LoadTensor1DInt8(string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int width = weight.size();
	DL::Tensor1D<int8>* params = new Tensor1D<int8>(width);

	for (int x = 0; x < width; x++)
	{
		int8 value = weight[x].asInt();
		params->Set(x, value);
	}
	return *params;
}

const DL::Tensor2D<int8>& ParamsLoader::LoadTensor2DInt8(std::string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int height = weight.size();
	int width = weight[0].size();
	DL::Tensor2D<int8>* params = new Tensor2D<int8>(width, height);

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			int8 value = weight[y][x].asInt();
			params->Set(x, y, value);
		}
	}
	return *params;
}

const DL::Tensor3D<int8>& ParamsLoader::LoadTensor3DInt8(std::string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int channel = weight.size();
	int height = weight[0].size();
	int width = weight[0][0].size();
	DL::Tensor3D<int8>* params = new Tensor3D<int8>(width, height, channel);
	for (int c = 0; c < channel; c++)
	{
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				int8 value = weight[c][y][x].asInt();
				params->Set(x, y, c, value);
			}
		}
	}
	return *params;
}

const DL::Tensor4D<int8>& ParamsLoader::LoadTensor4DInt8(std::string name)
{
	Json::Value weight = GetLayerValueByName(name);
	int deepth = weight.size();
	int channel = weight[0].size();
	int height = weight[0][0].size();
	int width = weight[0][0][0].size();
	DL::Tensor4D<int8>* params = new DL::Tensor4D<int8>(width, height, channel, deepth);

	for (int d = 0; d < deepth; d++)
	{
		for (int c = 0; c < channel; c++)
		{
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					int8 value = weight[d][c][y][x].asInt();
					params->Set(x, y, c, d, value);
				}
			}
		}
	}
	return *params;
}

float ParamsLoader::LoadfloatValue(std::string name) {
	Json::Value value = GetLayerValueByName(name);
	return value.asFloat();
}

int ParamsLoader::LoadIntValue(std::string name)
{
	Json::Value value = GetLayerValueByName(name);
	return value.asInt();
}

float GetAccurateValue(Json::Value value) {
	return value.asFloat();
}
